// golang生成的代码。org/x/tools/cmd/bundle。不要编辑。
// go:generate bundle-o socks_bundle。go-golang。org/x/net/internal/socks 

// 包socks提供了socks版本5的客户端实现。
// 
// SOCKS协议版本5在RFC 1928中定义。
// SOCKS版本5的用户名/密码身份验证在
// RFC 1929中定义。
// 

package http

import (
	"context"
	"errors"
	"io"
	"net"
	"strconv"
	"time"
)

var (
	socksnoDeadline   = time.Time{}
	socksaLongTimeAgo = time.Unix(1, 0)
)

func (d *socksDialer) connect(ctx context.Context, c net.Conn, address string) (_ net.Addr, ctxErr error) {
	host, port, err := sockssplitHostPort(address)
	if err != nil {
		return nil, err
	}
	if deadline, ok := ctx.Deadline(); ok && !deadline.IsZero() {
		c.SetDeadline(deadline)
		defer c.SetDeadline(socksnoDeadline)
	}
	if ctx != context.Background() {
		errCh := make(chan error, 1)
		done := make(chan struct{})
		defer func() {
			close(done)
			if ctxErr == nil {
				ctxErr = <-errCh
			}
		}()
		go func() {
			select {
			case <-ctx.Done():
				c.SetDeadline(socksaLongTimeAgo)
				errCh <- ctx.Err()
			case <-done:
				errCh <- nil
			}
		}()
	}

	b := make([]byte, 0, 6+len(host)) // 这里的大小只是一个估计值
	b = append(b, socksVersion5)
	if len(d.AuthMethods) == 0 || d.Authenticate == nil {
		b = append(b, 1, byte(socksAuthMethodNotRequired))
	} else {
		ams := d.AuthMethods
		if len(ams) > 255 {
			return nil, errors.New("too many authentication methods")
		}
		b = append(b, byte(len(ams)))
		for _, am := range ams {
			b = append(b, byte(am))
		}
	}
	if _, ctxErr = c.Write(b); ctxErr != nil {
		return
	}

	if _, ctxErr = io.ReadFull(c, b[:2]); ctxErr != nil {
		return
	}
	if b[0] != socksVersion5 {
		return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0])))
	}
	am := socksAuthMethod(b[1])
	if am == socksAuthMethodNoAcceptableMethods {
		return nil, errors.New("no acceptable authentication methods")
	}
	if d.Authenticate != nil {
		if ctxErr = d.Authenticate(ctx, c, am); ctxErr != nil {
			return
		}
	}

	b = b[:0]
	b = append(b, socksVersion5, byte(d.cmd), 0)
	if ip := net.ParseIP(host); ip != nil {
		if ip4 := ip.To4(); ip4 != nil {
			b = append(b, socksAddrTypeIPv4)
			b = append(b, ip4...)
		} else if ip6 := ip.To16(); ip6 != nil {
			b = append(b, socksAddrTypeIPv6)
			b = append(b, ip6...)
		} else {
			return nil, errors.New("unknown address type")
		}
	} else {
		if len(host) > 255 {
			return nil, errors.New("FQDN too long")
		}
		b = append(b, socksAddrTypeFQDN)
		b = append(b, byte(len(host)))
		b = append(b, host...)
	}
	b = append(b, byte(port>>8), byte(port))
	if _, ctxErr = c.Write(b); ctxErr != nil {
		return
	}

	if _, ctxErr = io.ReadFull(c, b[:4]); ctxErr != nil {
		return
	}
	if b[0] != socksVersion5 {
		return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0])))
	}
	if cmdErr := socksReply(b[1]); cmdErr != socksStatusSucceeded {
		return nil, errors.New("unknown error " + cmdErr.String())
	}
	if b[2] != 0 {
		return nil, errors.New("non-zero reserved field")
	}
	l := 2
	var a socksAddr
	switch b[3] {
	case socksAddrTypeIPv4:
		l += net.IPv4len
		a.IP = make(net.IP, net.IPv4len)
	case socksAddrTypeIPv6:
		l += net.IPv6len
		a.IP = make(net.IP, net.IPv6len)
	case socksAddrTypeFQDN:
		if _, err := io.ReadFull(c, b[:1]); err != nil {
			return nil, err
		}
		l += int(b[0])
	default:
		return nil, errors.New("unknown address type " + strconv.Itoa(int(b[3])))
	}
	if cap(b) < l {
		b = make([]byte, l)
	} else {
		b = b[:l]
	}
	if _, ctxErr = io.ReadFull(c, b); ctxErr != nil {
		return
	}
	if a.IP != nil {
		copy(a.IP, b)
	} else {
		a.Name = string(b[:len(b)-2])
	}
	a.Port = int(b[len(b)-2])<<8 | int(b[len(b)-1])
	return &a, nil
}

func sockssplitHostPort(address string) (string, int, error) {
	host, port, err := net.SplitHostPort(address)
	if err != nil {
		return "", 0, err
	}
	portnum, err := strconv.Atoi(port)
	if err != nil {
		return "", 0, err
	}
	if 1 > portnum || portnum > 0xffff {
		return "", 0, errors.New("port number out of range " + port)
	}
	return host, portnum, nil
}

// 一个命令代表一个SOCKS命令。
type socksCommand int

func (cmd socksCommand) String() string {
	switch cmd {
	case socksCmdConnect:
		return "socks connect"
	case sockscmdBind:
		return "socks bind"
	default:
		return "socks " + strconv.Itoa(int(cmd))
	}
}

// AuthMethod表示SOCKS身份验证方法。
type socksAuthMethod int

// 回复代表SOCKS命令回复代码。
type socksReply int

func (code socksReply) String() string {
	switch code {
	case socksStatusSucceeded:
		return "succeeded"
	case 0x01:
		return "general SOCKS server failure"
	case 0x02:
		return "connection not allowed by ruleset"
	case 0x03:
		return "network unreachable"
	case 0x04:
		return "host unreachable"
	case 0x05:
		return "connection refused"
	case 0x06:
		return "TTL expired"
	case 0x07:
		return "command not supported"
	case 0x08:
		return "address type not supported"
	default:
		return "unknown code: " + strconv.Itoa(int(code))
	}
}

// 导线协议常数。
const (
	socksVersion5 = 0x05

	socksAddrTypeIPv4 = 0x01
	socksAddrTypeFQDN = 0x03
	socksAddrTypeIPv6 = 0x04

	socksCmdConnect socksCommand = 0x01 // 建立主动开放转发代理连接
	sockscmdBind    socksCommand = 0x02 // 建立被动开放转发代理连接

	socksAuthMethodNotRequired         socksAuthMethod = 0x00 // 无需身份验证
	socksAuthMethodUsernamePassword    socksAuthMethod = 0x02 // 使用用户名/密码
	socksAuthMethodNoAcceptableMethods socksAuthMethod = 0xff // 无可接受的身份验证方法

	socksStatusSucceeded socksReply = 0x00
)

// Addr表示SOCKS特定的地址。
// 只使用名称或IP。
type socksAddr struct {
	Name string // 完全限定域名
	IP   net.IP
	Port int
}

func (a *socksAddr) Network() string { return "socks" }

func (a *socksAddr) String() string {
	if a == nil {
		return "<nil>"
	}
	port := strconv.Itoa(a.Port)
	if a.IP == nil {
		return net.JoinHostPort(a.Name, port)
	}
	return net.JoinHostPort(a.IP.String(), port)
}

// Conn表示前向代理连接。
type socksConn struct {
	net.Conn

	boundAddr net.Addr
}

// BoundAddr返回代理服务器为
// 从代理服务器连接到命令目标地址而分配的地址。
func (c *socksConn) BoundAddr() net.Addr {
	if c == nil {
		return nil
	}
	return c.boundAddr
}

// 拨号器包含特定选项。
type socksDialer struct {
	cmd          socksCommand // CmdConnect或cmdBind 
	proxyNetwork string       // 代理服务器和客户端之间的网络
	proxyAddress string       // 代理服务器地址

	// ProxyDial指定
	// 建立传输连接的可选拨号函数。
	ProxyDial func(context.Context, string, string) (net.Conn, error)

	// AuthMethods指定请求身份验证的列表
	// 方法。
	// 如果为空，SOCKS客户端只请求AuthMethodNotRequired。
	AuthMethods []socksAuthMethod

	// Authenticate指定可选的身份验证
	// 函数。当AuthMethods不为空时，它必须为非nil。
	// 身份验证失败时必须返回错误。
	Authenticate func(context.Context, io.ReadWriter, socksAuthMethod) error
}

// DialContext连接到提供的
// 网络上提供的地址。
// 
// 返回的错误值可能是净值。歌剧演员。当
// net时。OpError包含“socks”，源字段包含代理
// 服务器地址，Addr字段包含命令目标
// 地址。
// 
// 有关网络和地址参数的说明，请参见标准库网络包的func Dial。
func (d *socksDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
	if err := d.validateTarget(network, address); err != nil {
		proxy, dst, _ := d.pathAddrs(address)
		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
	}
	if ctx == nil {
		proxy, dst, _ := d.pathAddrs(address)
		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
	}
	var err error
	var c net.Conn
	if d.ProxyDial != nil {
		c, err = d.ProxyDial(ctx, d.proxyNetwork, d.proxyAddress)
	} else {
		var dd net.Dialer
		c, err = dd.DialContext(ctx, d.proxyNetwork, d.proxyAddress)
	}
	if err != nil {
		proxy, dst, _ := d.pathAddrs(address)
		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
	}
	a, err := d.connect(ctx, c, address)
	if err != nil {
		c.Close()
		proxy, dst, _ := d.pathAddrs(address)
		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
	}
	return &socksConn{Conn: c, boundAddr: a}, nil
}

// DialWithConn使用已连接到SOCKS服务器的连接c，启动从SOCKS服务器到目标
// 网络和地址的连接。
// 
// 返回SOCKS 
// 服务器分配的连接本地地址。
func (d *socksDialer) DialWithConn(ctx context.Context, c net.Conn, network, address string) (net.Addr, error) {
	if err := d.validateTarget(network, address); err != nil {
		proxy, dst, _ := d.pathAddrs(address)
		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
	}
	if ctx == nil {
		proxy, dst, _ := d.pathAddrs(address)
		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
	}
	a, err := d.connect(ctx, c, address)
	if err != nil {
		proxy, dst, _ := d.pathAddrs(address)
		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
	}
	return a, nil
}

// 拨号连接到提供的网络上提供的地址。与DialContext不同，它返回的是原始传输连接，而不是前向代理连接的原始传输连接。
// 
// 不推荐使用：改用DialContext或DialWithConn。
func (d *socksDialer) Dial(network, address string) (net.Conn, error) {
	if err := d.validateTarget(network, address); err != nil {
		proxy, dst, _ := d.pathAddrs(address)
		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
	}
	var err error
	var c net.Conn
	if d.ProxyDial != nil {
		c, err = d.ProxyDial(context.Background(), d.proxyNetwork, d.proxyAddress)
	} else {
		c, err = net.Dial(d.proxyNetwork, d.proxyAddress)
	}
	if err != nil {
		proxy, dst, _ := d.pathAddrs(address)
		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
	}
	if _, err := d.DialWithConn(context.Background(), c, network, address); err != nil {
		c.Close()
		return nil, err
	}
	return c, nil
}

func (d *socksDialer) validateTarget(network, address string) error {
	switch network {
	case "tcp", "tcp6", "tcp4":
	default:
		return errors.New("network not implemented")
	}
	switch d.cmd {
	case socksCmdConnect, sockscmdBind:
	default:
		return errors.New("command not implemented")
	}
	return nil
}

func (d *socksDialer) pathAddrs(address string) (proxy, dst net.Addr, err error) {
	for i, s := range []string{d.proxyAddress, address} {
		host, port, err := sockssplitHostPort(s)
		if err != nil {
			return nil, nil, err
		}
		a := &socksAddr{Port: port}
		a.IP = net.ParseIP(host)
		if a.IP == nil {
			a.Name = host
		}
		if i == 0 {
			proxy = a
		} else {
			dst = a
		}
	}
	return
}

// NewDialer返回通过提供的
// 代理服务器的网络和地址拨号的新拨号程序。
func socksNewDialer(network, address string) *socksDialer {
	return &socksDialer{proxyNetwork: network, proxyAddress: address, cmd: socksCmdConnect}
}

const (
	socksauthUsernamePasswordVersion = 0x01
	socksauthStatusSucceeded         = 0x00
)

// 用户名密码是用户名/密码的凭据
// 身份验证方法。
type socksUsernamePassword struct {
	Username string
	Password string
}

// Authenticate使用
// 代理服务器对一对用户名和密码进行身份验证。
func (up *socksUsernamePassword) Authenticate(ctx context.Context, rw io.ReadWriter, auth socksAuthMethod) error {
	switch auth {
	case socksAuthMethodNotRequired:
		return nil
	case socksAuthMethodUsernamePassword:
		if len(up.Username) == 0 || len(up.Username) > 255 || len(up.Password) == 0 || len(up.Password) > 255 {
			return errors.New("invalid username/password")
		}
		b := []byte{socksauthUsernamePasswordVersion}
		b = append(b, byte(len(up.Username)))
		b = append(b, up.Username...)
		b = append(b, byte(len(up.Password)))
		b = append(b, up.Password...)
		// TODO（mikio）：处理IO截止日期和取消，如果
		// 必要的
		if _, err := rw.Write(b); err != nil {
			return err
		}
		if _, err := io.ReadFull(rw, b[:2]); err != nil {
			return err
		}
		if b[0] != socksauthUsernamePasswordVersion {
			return errors.New("invalid username/password version")
		}
		if b[1] != socksauthStatusSucceeded {
			return errors.New("username/password authentication failed")
		}
		return nil
	}
	return errors.New("unsupported authentication method " + strconv.Itoa(int(auth)))
}
